/*
NAME : RUSHIKESH FANSE
ROLL : 16
DIV  : H
ASSIGNMENT NO 3 : HILL CHIPHER FOR 3*3 MATRIX
*/

#include<cstdio>
#include<iostream>
#include<vector>

//calculate the minor elements of matrix in advance
#define simplify1 ((enc_mat[1][1]*enc_mat[2][2])-(enc_mat[1][2]*enc_mat[2][1]))
#define simplify2 ((enc_mat[1][0]*enc_mat[2][2])-(enc_mat[1][2]*enc_mat[2][0]))
#define simplify3 ((enc_mat[1][0]*enc_mat[2][1])-(enc_mat[1][1]*enc_mat[2][0]))
#define simplify4 ((enc_mat[0][1]*enc_mat[2][2])-(enc_mat[0][2]*enc_mat[2][1]))
#define simplify5 ((enc_mat[0][0]*enc_mat[2][2])-(enc_mat[0][2]*enc_mat[2][0]))
#define simplify6 ((enc_mat[0][0]*enc_mat[2][1])-(enc_mat[0][1]*enc_mat[2][0]))
#define simplify7 ((enc_mat[0][1]*enc_mat[1][2])-(enc_mat[0][2]*enc_mat[1][1]))
#define simplify8 ((enc_mat[0][0]*enc_mat[1][2])-(enc_mat[0][2]*enc_mat[1][0]))
#define simplify9 ((enc_mat[0][0]*enc_mat[1][1])-(enc_mat[0][1]*enc_mat[1][0]))
#define ps push_back 

//we know number of alphabet is 26
#define NUMBER_OF_ALPHABET 26 
using namespace std;

class HillChipher{

	//first matrix is the input matrix  
	//second matrix is encryption matrix
	//third matrix is for the result of encryption
	int in_mat[3],enc_mat[3][3],res[3];
	//decryption matrix 
	//and output matrix
	int dec_mat[3][3],out_mat[3];

	//cofactor matrix
	int cofactor_mat[3][3];

	//transpose matrix
	int transpose_mat[3][3];

	//det value is stored in this variable;
	int det;

	//this are the functions used to find the inverse matrix for decryption
		void find_determinant(){
			det=enc_mat[0][0]*simplify1-enc_mat[0][1]*simplify2+enc_mat[0][2]*simplify3;
		}


		void find_cofactor(){
			cofactor_mat[0][0]=simplify1;
			cofactor_mat[0][1]=0-simplify2;
			cofactor_mat[0][2]=simplify3;
			cofactor_mat[1][0]=0-simplify4;
			cofactor_mat[1][1]=simplify5;
			cofactor_mat[1][2]=0-simplify6;
			cofactor_mat[2][0]=simplify7;
			cofactor_mat[2][1]=0-simplify8;
			cofactor_mat[2][2]=simplify9;
		}


		void find_traspose(){
			for(int i=0;i<3;i++){
				for(int j=0;j<3;j++){
					transpose_mat[j][i]=cofactor_mat[i][j];
				}
			}
		}

		bool necessary(){
			int flag=false;
			for(int i=0;i<3;i++){
				for(int j=0;j<3;j++){
					if((cofactor_mat[i][j]%det)!=0){
						return true;
					}
				}
			}
			return false;
		}

		void find_modular_multiplicative_inverse(){
			//we first check whether the operation is necessary 
			//if yes we continue else we find the adjacent matrix 

			vector<int> a,b,c,d;
			bool val=false;
			if(val=necessary()){
				
				//we know the range already so we peform the mod operation and we
				//check for what value of x does we get 1 for modulus that is our answer
				//it is the equation for modular inverse which is given as follows
				//ax=1(mod b) here a is the determinant value and m is the number of
				//alphabets
				int x;
				while(det<-NUMBER_OF_ALPHABET)
					det = det%NUMBER_OF_ALPHABET;
				if(det<0)
					det=det+NUMBER_OF_ALPHABET;
				for (x=1; x<NUMBER_OF_ALPHABET; x++)
					if ((det*x) % NUMBER_OF_ALPHABET == 1)
						break;
				det=x;
				find_adjacent_mat(val);
			}
			else
				find_adjacent_mat(val);
		}

		void find_adjacent_mat(bool op){
			for(int i=0;i<3;i++){
				for(int j=0;j<3;j++){
					if(op){
						dec_mat[i][j]=(transpose_mat[i][j]*det)%26;
						if(dec_mat[i][j]<0)
							dec_mat[i][j]=dec_mat[i][j]+26;
					}
					else{
						dec_mat[i][j]=(transpose_mat[i][j]/det)%26;		
						if(dec_mat[i][j]<0)
							dec_mat[i][j]=dec_mat[i][j]+26;
					}
				}
			}
		}
		void find_inverse(){
			find_determinant();
			find_cofactor();
			find_traspose();
			find_modular_multiplicative_inverse();
		}
		int char_to_int(char ch){
			return (int)ch-97;
		}

		char int_to_char(int in){
			return (char)(in+97);
		}
	public:
		enum operation{encryption,decryption};

		void accept_input_matrix(){
			char ch;
			for(int i=0;i<3;i++){
				cin>>ch;
				in_mat[i]=char_to_int(ch);
			}
		}
		void accept_encryption_matrix(){
			char ch;
			for(int i=0;i<3;i++){
				for(int j=0;j<3;j++){
					cin>>enc_mat[i][j];
					enc_mat[i][j]%=26;
				}
			}
		}
		void encryption_operation(){
			for(int i=0;i<3;i++){
				res[i]=(in_mat[0]*enc_mat[0][i]+in_mat[1]*enc_mat[1][i]+in_mat[2]*enc_mat[2][i])%26; 
			}
		}
		void decryption_operation(){
			//find the inverse of the given encryption matrix so as to decode the 
			//encrypted matrix
			find_inverse();
			for(int i=0;i<3;i++){
				out_mat[i]=(res[0]*dec_mat[0][i]+res[1]*dec_mat[1][i]+res[2]*dec_mat[2][i])%26; 	
			}
		}
		void display_matrix(int operate){

			switch(operate){
				//print the matrix for encryption
				case encryption:
					for(int i=0;i<3;i++){
						cout<<int_to_char(res[i])<<" ";
					}
					cout<<endl;
				break;

				//print the matrix for decryption
				case decryption:
					for(int i=0;i<3;i++){
						cout<<int_to_char(out_mat[i])<<" ";
					}
					cout<<endl;
				break;
			}
		}
		

};


void start(){

	//create the object of hill chipher
	HillChipher hc;

	//accept the input matrix
	cout<<"Enter the input matrix"<<endl;
	hc.accept_input_matrix();

	//accept the key matrix
	cout<<"Enter the encryption matrix"<<endl;
	hc.accept_encryption_matrix();

	//do the encryption operation
	hc.encryption_operation();

	//display the encrypted matrix
	cout<<"Encrypted matrix is "<<endl;
	hc.display_matrix(HillChipher::encryption);

	//do the decryption operation
	hc.decryption_operation();

	//display the decrypted matrix 
	cout<<"Decrypted matrix is "<<endl;
	hc.display_matrix(HillChipher::decryption);
}

int main(){
	//the code works only for small characters
	//start program
	start();
	return 0;
}
/*
OUTPUT:

rishi@rishi-PC:~$ ./a.out
Enter the input matrix
ned
Enter the encryption matrix
9 4 8
5 7 5
6 8 1
Encrypted matrix is 
z a x 
Decrypted matrix is 
n e d 

*/